import 'dart:math';

import 'package:flutter/material.dart';

class AppCircularProgressIndicator extends StatelessWidget {
  const AppCircularProgressIndicator({
    required this.radius,
    required this.thickness,
    required this.progress,
    required this.backgroundColor,
    required this.progressColor,
    required this.roundedCap,
    this.center,
    super.key,
  });

  final double radius;
  final double thickness;
  final double progress;
  final Color backgroundColor;
  final Color progressColor;
  final bool roundedCap;
  final Widget? center;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: radius * 2,
      height: radius * 2,
      child: CustomPaint(
        painter: _CircularProgressBarPainter(
          radius: radius,
          thickness: thickness,
          progress: progress,
          backgroundColor: backgroundColor,
          roundedCap: roundedCap,
          progressColor: progressColor,
        ),
        child: center == null
            ? Container()
            : Center(
                child: center,
              ),
      ),
    );
  }
}

class _CircularProgressBarPainter extends CustomPainter {
  _CircularProgressBarPainter({
    required this.radius,
    required this.thickness,
    required this.progress,
    required this.backgroundColor,
    required this.progressColor,
    required this.roundedCap,
  });

  final double radius;
  final double thickness;
  final double progress;
  final Color backgroundColor;
  final Color progressColor;
  final bool roundedCap;

  @override
  void paint(Canvas canvas, Size size) {
    final backgroundPaint = Paint()
      ..color = backgroundColor
      ..strokeWidth = thickness
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    final customCapPaint = Paint()
      ..color = progressColor
      ..style = PaintingStyle.fill;

    final progressRoundPaint = Paint()
      ..color = progressColor
      ..strokeWidth = thickness
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    final progressButtPaint = Paint()
      ..color = progressColor
      ..strokeWidth = thickness
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.butt;

    final a = thickness / 2;
    final b = radius - thickness;
    final alpha = pi * progress;
    final capCorner = asin(a / (a + b));

    final capsProgress = capCorner / pi;

    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      radius - thickness / 2,
      backgroundPaint,
    );

    if (progress == 0) {
      return;
    }

    if (progress == 1.0) {
      canvas.drawCircle(
        Offset(size.width / 2, size.height / 2),
        radius - thickness / 2,
        progressButtPaint,
      );
    } else if (roundedCap) {
      if (progress >= capsProgress) {
        canvas.drawArc(
          Rect.fromCircle(
            center: Offset(size.width / 2, size.height / 2),
            radius: radius - thickness / 2,
          ),
          -pi / 2 + capCorner,
          (progress - capsProgress) * 2 * pi,
          false,
          progressRoundPaint,
        );
      } else {
        final r = ((a + b) - sqrt(b * (2 * a + b) * pow(cos(alpha), 2))) / sin(alpha);
        final beta = asin(a / r);

        canvas.drawArc(
          Rect.fromCircle(
            center: Offset(
              size.width / 2 + r,
              size.height / 2 - (a + b - r * sin(alpha)) / cos(alpha),
            ),
            radius: r,
          ),
          pi + alpha - beta,
          2 * beta,
          false,
          customCapPaint,
        );

        canvas.drawArc(
          Rect.fromCircle(
            center: Offset(
              size.width / 2 - (sqrt(r * r - a * a) * cos(alpha) - (a + b) * sin(alpha)),
              size.height / 2 - (sqrt(r * r - a * a) * sin(alpha) + (a + b) * cos(alpha)),
            ),
            radius: r,
          ),
          alpha - beta,
          2 * beta,
          false,
          customCapPaint,
        );
      }
    } else {
      canvas.drawArc(
        Rect.fromCircle(
          center: Offset(size.width / 2, size.height / 2),
          radius: radius - thickness / 2,
        ),
        -pi / 2,
        progress * 2 * pi,
        false,
        progressButtPaint,
      );
    }
  }

  @override
  bool shouldRepaint(_CircularProgressBarPainter oldDelegate) {
    return progress != oldDelegate.progress;
  }
}
